package com.admin.system.security.config;

import com.admin.model.dto.AuthUserDto;
import com.admin.system.security.jwt.JWTAuthenticationFilter;
import com.admin.system.security.jwt.JwtTokenUtils;
import com.admin.system.security.jwt.SecurityUtils;
import com.admin.system.security.service.UserDetailsImpl;
import com.admin.system.security.service.UserDetailsServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author caixiaofeng
 * @version 1.0
 * @Description: <<>>
 * @company cxf
 * @create 2021-01-27 22:13
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true, securedEnabled = true, jsr250Enabled = true)
//保证post之前的注解可以使用
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Resource
    private JWTAuthenticationFilter jwtAuthenticationFilter;
    @Resource
    private UserDetailsServiceImpl userDetailsService;
    @Resource
    private JwtTokenUtils jwtTokenUtils;
    private ObjectMapper objectMapper = new ObjectMapper();


    /**
     * 解决跨域问题
     *
     * @return
     */
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.addAllowedOrigin("*");
        //configuration.setAllowedOrigins(Arrays.asList("http://localhost:8080", "http://localhost:9528", "http://localhost:9527"));
        configuration.addAllowedMethod("*");
        //configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "HEAD", "PATCH", "OPTIONS", "TRACE"));
        configuration.applyPermitDefaultValues();
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    public void configure(WebSecurity web) {
        web.ignoring()
                // 放行静态资源
                .antMatchers(HttpMethod.GET,
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js",
                        "/**/*.ico",
                        "/webSocket/**")
                // 放行swagger api接口
                .antMatchers(
                        "/doc.html",
                        "/v2/api-docs-ext",
                        "/webjars/**",
                        "/*/favicon.ico",
                        "/*/api-docs",
                        "/swagger-resources/**",
                        "/v2/api-docs/**",
                        "/swagger-ui.html",
                        "/swagger-resources/configuration/ui/**",
                        "/swagger-resources/configuration/security/**");
    }



    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .cors()
                .and()
                //关闭csrf保护
                .csrf().disable()
                //不使用session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                //授权异常处理
                .exceptionHandling()
                //身份认证失败处理,状态码401
                .authenticationEntryPoint(authenticationEntryPoint())
                //访问被拒绝处理，状态码403
                .accessDeniedHandler(accessDeniedHandler())
                .and()
                //认证请求
                .authorizeRequests()
                //放行预请求
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                //放行
                .antMatchers("/auth/login", "/auth/logout", "/auth/refreshToken", "/auth/info", "/doc.html").permitAll()
                //其他所有请求都需要认证
                .anyRequest().authenticated()
                .and()
                .headers().cacheControl();
        http.addFilterBefore(jwtAuthenticationFilter,UsernamePasswordAuthenticationFilter.class);
    }

    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    /**
     * 身份认证失败处理401
     *
     * @return
     */
    @Bean
    AuthenticationEntryPoint authenticationEntryPoint() {
        return (httpServletRequest, httpServletResponse, e) -> {
            httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage() == null ? "Unauthorized" : e.getMessage());
        };
    }

    /**
     * 访问被拒绝处理403
     *
     * @return
     */
    @Bean
    AccessDeniedHandler accessDeniedHandler() {
        return (httpServletRequest, httpServletResponse, e) -> {
            httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
        };
    }

    /**
     * 身份验证失败处理程序
     *
     * @return
     */
    //@Bean
    AuthenticationFailureHandler authenticationFailureHandler() {
        return (request, response, e) -> {
            //找不到用户名异常
            if (e instanceof UsernameNotFoundException) {
                response.sendError(HttpServletResponse.SC_SEE_OTHER, e.getMessage());
            }
            //用户已经被锁定异常
            if(e instanceof LockedException){
                response.sendError(HttpServletResponse.SC_SEE_OTHER, e.getMessage());
            }
            //账号或密码错误异常
            if (e instanceof BadCredentialsException){
                response.sendError(HttpServletResponse.SC_SEE_OTHER, e.getMessage());
            }
            response.sendError(HttpServletResponse.SC_SEE_OTHER, e.getMessage());
        };
    }

    //@Bean
    AuthenticationSuccessHandler authenticationSuccessHandler(){
        return new AuthenticationSuccessHandler() {
            @Override
            public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                UserDetailsImpl UserDetailsImpl = (UserDetailsImpl) SecurityUtils.getCurrentUser();
                String token = jwtTokenUtils.generateToken(SecurityUtils.getCurrentUsername());
                //封装token对象信息
                HashMap<String, Object> userInfo = new HashMap<>();
                userInfo.put("token", token);
                userInfo.put("user", UserDetailsImpl.getUser());
                Set<String> roles = UserDetailsImpl.getAuthorities().stream()
                        .map(GrantedAuthority::getAuthority)
                        .collect(Collectors.toSet());
                userInfo.put("roles", roles);
                //json序列化
                String json = objectMapper.writeValueAsString(userInfo);
                response.setContentType("application/json;charset=UTF-8");
                PrintWriter writer = response.getWriter();
                writer.write(json);
                writer.flush();
                writer.close();
            }
        };
    }


}
